/**
 * 柯里化：函数柯里化又叫部分求值
 * 用大白话来说就是只传递给函数一部分参数来调用它，让它返回一个新函数去处理剩下的参数。
 */

// 基础版本: 两数相加
const sum = (a) => {
  return (b) => {
    return a + b
  }
}

// console.log(sum(1)(2))

// 自动化柯里化

const add = (function (...rest) {
  //定义一个闭包保存_args
  let _args = []
  return (...rest) => {
    if (!rest || !rest.length) {
      // 不传参数了，则执行最终逻辑
      let res = _args.reduce((sum, item) => sum + item)
      // 清空闭包，一方面回收资源，另一方面可以重新开始
      _args = []
      return res
    } else {
      _args.push(...rest)

      // 锁住 args 闭包
      return _args
    }
  }
})()

add(1)
add(2)
add(3)
add(4)
// console.log(add())
add(2)
add(4)
add(10)
// 如果没有清空 _args 的话，会返回 26
// console.log(add())

// 将一个普通函数转换成柯里化函数
// 主要是思路
const _add = (a, b, c, d, e) => {
  return a + b + c + d + e
}

const currying = (fn) => {
  return function curry(...args) {
    if (args.length >= fn.length) return fn.call(null, ...args)
    else return (..._args) => curry.apply(null, [...args, ..._args])
  }
}

let currySum = currying(_add)
// console.log(currySum(1, 2)(3)(4)(5, 6)(2))
// 只要是参数不够，就可以一直调用下去，但是一旦够了就会报错了

/**
 * console.log(currySum(1,2)(3)(4)(5,6)(2))
 *                                     ^
 * currySum(...)(...)(...)(...) is not a function
 */

/**
 * 管道模式
 * 类似数据柯里化，将原始数据进行一次次处理，返回最终处理结果
 * 中间异常则终止处理流，并返回中间值
 * 类似 express 的 next 函数
 * 我们模拟一个服务端处理接口响应的流程
 * 1- 接口守卫，即进入数据处理之前，实际逻辑里面会进行 数据结构校验、鉴权、安全校验、限流、降级等等
 * 2- 路由处理数据
 * 3- 数据过滤，即路由主要逻辑完成之后，实际逻辑里面会进行 返回数据加密、统一返回体、统一状态码、错误优化、异常搜集、日志记录等等
 */

// -----------------------
// --使用-----------------|>>>>
// -----------------------
class Router {
  constructor() {
    this.req = {
      body: null,
      data: null,
    }
    this.res = null

    this.guardList = []
    this.router = null
    this.filterList = []
    this.routes = []
  }

  addGuard(fn) {
    this.guardList.push(fn)
  }

  addFilter(fn) {
    this.filterList.push(fn)
  }

  mock(method, url, data) {
    this.req.data = data
    let _route = this.routes.filter(
      (item) => item.url === url && item.method === method
    )

    if (!_route.length) {
      return {
        code: '404',
        msg: '接口不存在！',
        data: null,
      }
    } else {
      this.router = _route[0].fn
      return this.service()
    }
  }

  addRoutes(method, url, fn) {
    console.log('')
    console.log('【添加路由】')
    console.log(`-|- ${method} | ${url}`)
    this.routes.push({ method, url, fn })
  }

  over(data){
    this.req = {body: null, data: null}
    this.res = null
    this.isGuard = false

    return data
  }

  service() {
    console.log('')
    console.log('【执行守卫】')
    if (this.guardList.length) {
      for (let i = 0; i < this.guardList.length; i++) {
        console.log('- 守卫', i)
        this.iaGuard = this.guardList[i](this.req, this.res)
        if (this.iaGuard === true) {
          continue
        } else {
          return this.over(this.iaGuard)
        }
      }
    }

    let _result
    console.log('')
    console.log('【执行路由逻辑】')
    this.res = this.router(this.req, this.res)

    console.log('')
    console.log('【执行过滤】')
    if (this.filterList.length) {
      for (let i = 0; i < this.filterList.length; i++) {
        console.log('- 过滤', i)
        _result = this.filterList[i](this.req, this.res)
      }
    }

    return this.over(_result)
  }
}

// 单例模式 创建 request
const createRouter = (function () {
  let instance

  return function () {
    if (!instance) {
      instance = new Router()
    }
    return instance
  }
})()

// -----------------------
// --使用-----------------|>>>>
// -----------------------

const router = createRouter()

router.addGuard((req, res) => {
  if (!req || !req.data || !req.data.name) {
    console.log('鉴权失败！')
    return {
      code: 403,
      msg: '未登录或没有权限',
      error: 'Forbidden',
    }
  } else {
    console.log('-|- 当前登录用户为:', req.data.name, '| 通过')
    return true
  }
})

router.addGuard((req, res) => {
  if (!req || !req.data || req.data.role !== 1) {
    console.log('权限不够！')

    return {
      code: 401,
      msg: '权限不够',
      error: 'Forbidden',
    }
  } else {
    console.log('-|- 当前用户角色为:', req.data.role, '| 通过')
    return true
  }
})

router.addFilter((req, res) => {
  console.log('-|- 过滤前 res:', res)
  return {
    code: 1,
    msg: res.msg,
    data: res.data,
  }
})

router.addRoutes('post', '/test', (req, res) => {
  let _data = req.data
  console.log(_data)

  return {
    msg: '操作成功111',
    data: '执行结果111',
  }
})
router.addRoutes('post', '/test1', (req, res) => {
  let _data = req.data
  console.log(_data)

  return {
    msg: '操作成功222',
    data: '执行结果222',
  }
})

console.log('')
console.log('【发起请求】 >>>>>>>>>>>>>>>>>>>')
let result = router.mock('post', '/test', { name: 'Tom', role: 1 })
console.log('')
console.log('【接口响应】 <<<<<<<<<<<<<<<<<<<<<')
console.log(result)

console.log('')
console.log('【发起请求】 >>>>>>>>>>>>>>>>>>>')
let result1 = router.mock('post', '/test1', { name: 'Jhon', role: 1 })
console.log('')
console.log('【接口响应】 <<<<<<<<<<<<<<<<<<<<<')
console.log(result1)

console.log('')
console.log('【发起请求】 >>>>>>>>>>>>>>>>>>>')
let result2 = router.mock('get', '/test1', { name: 'Jhon', role: 1 })
console.log('')
console.log('【接口响应】 <<<<<<<<<<<<<<<<<<<<<')
console.log(result2)